package think.in.java.concurrency.PractiseOneMoreTime;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Create an example of a busy wait.
 * One task sleeps for a while and then sets a flag to true.
 * The second task watches that flag inside a while loop (this is the busy wait) and when the flag becomes true,
 * sets it back to false and reports the change to the console.
 * Note how much wasted time the program spends inside the busy wait, and create a second version of the program that uses wait( ) instead of the busy wait
 */
public class Exersice22BusyWait {
    private long timer = 0;

    public static void main(String[] args) throws InterruptedException {
        Exersice22BusyWait exersice22BusyWait = new Exersice22BusyWait();
        exersice22BusyWait.testWithWhile();
        exersice22BusyWait.testWithWait();
    }

    public void testWithWhile() throws InterruptedException {
        Flag flag = new Flag();
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.execute(new BusyWaiterWithWhile(flag));
        Thread.sleep(100);
        executorService.execute(new NotifierWithWhile(flag));
        Thread.sleep(100);
        executorService.shutdownNow();
    }

    public void testWithWait() throws InterruptedException {
        Flag flag = new Flag();
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.execute(new BusyWaiterWithWait(flag));
        Thread.sleep(100);
        executorService.execute(new NotifiorWithWait(flag));
        Thread.sleep(100);
        executorService.shutdownNow();
    }

    class BusyWaiterWithWhile implements Runnable {
        private Flag flag;

        BusyWaiterWithWhile(Flag flag) {
            this.flag = flag;
        }


        @Override
        public void run() {
            try {
                while (!Thread.interrupted()) {
                    if (flag.getFlag() == false) {
                        printTime();
                        flag.setFlag(true);
                    }

                    Thread.sleep(50);

                }
            } catch (InterruptedException e) {
            }
        }
    }

    class NotifierWithWhile implements Runnable {
        private Flag flag;

        NotifierWithWhile(Flag flag) {
            this.flag = flag;

        }

        @Override
        public void run() {
            flag.setFlag(false);
            setTimer();
        }
    }

    class BusyWaiterWithWait implements Runnable {
        private Flag flag;

        BusyWaiterWithWait(Flag flag) {
            this.flag = flag;
        }

        @Override
        public void run() {
            try {
                while (!Thread.interrupted()) {
                    synchronized (flag) {

                        flag.wait();
                        if (flag.getFlag() == false) {
                            printTime();
                            flag.setFlag(true);
                        }
                    }
                }
            } catch (InterruptedException e) {
            }
        }
    }

    class NotifiorWithWait implements Runnable {
        private Flag flag;

        NotifiorWithWait(Flag flag) {
            this.flag = flag;
        }

        @Override
        public void run() {
            flag.setFlag(false);
            setTimer();
            synchronized (flag){
                flag.notifyAll();
            }

        }
    }

    class Flag {
        private Boolean flag=true;

        public Boolean getFlag() {
            return flag;
        }

        public void setFlag(Boolean flag) {
            this.flag = flag;
        }
    }

    public void setTimer() {
        timer = System.currentTimeMillis();

    }

    public void printTime() {
        System.out.println("Used : " + (System.currentTimeMillis() - timer));
    }
}


